package com.jintian.smart.kernel.flowable.context;

import cn.hutool.core.builder.EqualsBuilder;
import cn.hutool.core.builder.HashCodeBuilder;
import com.jintian.smart.kernel.common.util.ObjectUtil;
import com.jintian.smart.kernel.core.auth.IOperator;
import com.jintian.smart.kernel.core.flow.FlowAction;
import com.jintian.smart.kernel.core.flow.IFlowRuntime;
import com.jintian.smart.kernel.core.flow.IProcessContext;
import com.jintian.smart.kernel.core.flow.ProcessControl;
import org.flowable.task.api.TaskInfo;

import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class DefaultProcessContext implements IProcessContext<List<TaskInfo>> {

    private String businessKey;

    private String processInstanceId;
    private String processLabel;

    private String taskId;

    private ProcessControl processControl;

    private List<TaskInfo> result;

    private List<TaskInfo> newTasks = new ArrayList<>();

    private final Map<String, Object> variables = new HashMap<>();
    private final Map<String, Object> transientVariables = new HashMap<>();

    protected final Map<String, Object> variablesLocal = new HashMap<>();
    protected final Map<String, Object> transientVariablesLocal = new HashMap<>();

    private Closeable fireClosed;

    private String process;

    private Integer processVersion;

    private String activity;

    private boolean processFinished;

    private FlowAction action;

    private Map<String, Object> attrubites = new HashMap<>();

    /**
     * 操作者即系统登录者
     */
    private IOperator operator;

    private IFlowRuntime flowRuntime;

    @Override
    public String getProcess() {
        return process;
    }

    @Override
    public String getActivity() {
        return activity;
    }

    public void setActivity(String activity) {
        this.activity = activity;
    }

    public void setProcess(String process) {
        this.process = process;
    }

    public void setFireClosed(Closeable closeable) {
        this.fireClosed = closeable;
    }

    @Override
    public List<TaskInfo> getResult() {
        return result;
    }

    public void setResult(List<TaskInfo> result) {
        this.result = result;
    }

    @Override
    public void close() throws IOException {
        fireClosed.close();
    }

    @Override
    public String getBusinessKey() {
        return businessKey;
    }

    @Override
    public String getProcessInstanceId() {
        return processInstanceId;
    }

    @Override
    public String getProcessLabel() {
        return processLabel;
    }

    @Override
    public String getTaskId() {
        return taskId;
    }

    public void setBusinessKey(String businessKey) {
        this.businessKey = businessKey;
    }

    public void setProcessInstanceId(String processInstanceId) {
        this.processInstanceId = processInstanceId;
    }

    public void setProcessLabel(String processLabel) {
        this.processLabel = processLabel;
    }

    public void setTaskId(String taskId) {
        this.taskId = taskId;
    }

    public FlowAction getAction() {
        return action;
    }

    public void setAction(FlowAction action) {
        this.action = action;
    }

    public Map<String, Object> getVariables() {
        return variables;
    }

    public Integer getProcessVersion() {
        return processVersion;
    }

    public void setProcessVersion(Integer processVersion) {
        this.processVersion = processVersion;
    }

    public Map<String, Object> getTransientVariables() {
        return transientVariables;
    }

    public Map<String, Object> getVariablesLocal() {
        return variablesLocal;
    }

    public Map<String, Object> getTransientVariablesLocal() {
        return transientVariablesLocal;
    }

    public void addNewTask(TaskInfo task) {
        this.newTasks.add(task);
    }

    public List<TaskInfo> getNewTasks() {
        return newTasks;
    }

    @Override
    public ProcessControl getProcessControl() {
        if (this.processControl == null) {
            this.processControl = new ProcessControl();
        }
        return this.processControl;
    }

    public void setProcessControl(ProcessControl processControl) {
        this.processControl = processControl;
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder().append(this.processInstanceId).append(this.processLabel).append(this.taskId)
                .append(this.businessKey).append(this.process).append(this.activity).append(this.action).build();
    }

    private static final String EMPTY = "";

    private String defaultProcessInstanceId() {
        return ObjectUtil.defaultIfNull(this.processInstanceId, EMPTY);
    }

    private String defaultTaskId() {
        return ObjectUtil.defaultIfNull(this.taskId, EMPTY);
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof IProcessContext)) {
            return false;
        }
        @SuppressWarnings("rawtypes")
        IProcessContext that = (IProcessContext) obj;
        return new EqualsBuilder()
                //
                .append(this.businessKey, that.getBusinessKey())
                //
                .append(this.defaultProcessInstanceId(), ObjectUtil.defaultIfNull(that.getProcessInstanceId(), EMPTY))
                //
                .append(this.defaultTaskId(), ObjectUtil.defaultIfNull(that.getTaskId(), EMPTY))
                //
                .append(this.getProcess(), that.getProcess())
                //
                .append(this.getActivity(), that.getActivity())
                //
                .append(this.getAction(), that.getAction()).build();
    }

    public String getKey() {
        return new StringBuffer()
                //
                .append("process=").append(this.process).append(",")
                //
                .append("activity=").append(this.activity).append(",")
                //
                .append("action=").append(this.action).append(",")
                //
                .append("processInstanceId=").append(this.processInstanceId).append(",")
                //
                .append("taskId=").append(this.taskId).append(",")
                //
                .append("businessKey=").append(this.businessKey).toString();
    }

    @Override
    public String toString() {
        return new StringBuffer().append("[").append(this.getKey()).append(",")
                //
                .append("operatorId=").append(this.getOperatorId()).append(",")
                //
                .append("operatorName=").append(this.getOperatorName())
                //
                .append("]").toString();
    }

    @Override
    public void putVariable(String key, Object value) {
        this.variables.put(key, value);
    }

    @Override
    public boolean containsVariable(String key) {
        return this.variables.containsKey(key);
    }

    @Override
    public void putLocalVariable(String key, Object value) {
        this.variablesLocal.put(key, value);
    }

    @Override
    public void putTransientVariable(String key, Object value) {
        this.transientVariables.put(key, value);
    }

    @Override
    public void putTransientLocalVariable(String key, Object value) {
        this.transientVariablesLocal.put(key, value);

    }

    public void setProcessFinished(boolean processFinished) {
        this.processFinished = processFinished;
    }

    @Override
    public boolean isProcessFinished() {
        return this.processFinished;
    }

    public void setOperator(IOperator operator) {
        this.operator = operator;
    }

    public IOperator getOperator() {
        return this.operator;
    }

    @Override
    public String getOperatorId() {
        return operator != null ? operator.getId() : null;
    }

    @Override
    public String getOperatorName() {
        if (isAgent()) {
            return FlowAction.getFlowOwner() + (this.operator != null ? "(" + this.operator.getName() + " 代)" : "");
        } else {
            return this.operator.getName();
        }
    }

    @Override
    public boolean isAgent() {
        if (FlowAction.getFlowOwner() == null)
            return false;
        return !FlowAction.getFlowOwner().getId().equals(this.operator.getId());
    }

    private String businessStatus;

    @Override
    public void updateBusinessStatus(String status) {
        this.businessStatus = status;
    }

    public String getBusinessStatus() {
        return this.businessStatus;
    }

    @Override
    public <E> E getAttribute(String name, Class<E> clazz) {
        return clazz.cast(this.attrubites.get(name));
    }

    @Override
    public void setAttribute(String name, Object value) {
        this.attrubites.put(name, value);
    }

    @Override
    public IFlowRuntime getFlowRuntime() {
        return this.flowRuntime;
    }

    public void setFlowRuntime(IFlowRuntime flowRuntime) {
        this.flowRuntime = flowRuntime;
    }
}
